#include <stdio.h>
#include <windows.h>
#include <process.h>
#include <assert.h>

#include "ThreadPool.h"


CThreadPool* CThreadPool::GetInstance()
{
	static CThreadPool m_Pool;

	return &m_Pool;
}

CThreadPool::CThreadPool() :
m_nTopIndex(0), m_nBottomIndex(0), m_nWorkInProgress(0), m_Thrdaddr(0)
{
	hEmptySlot = CreateSemaphore(NULL, MAXQUEUE, MAXQUEUE, "EmptySlot");
	hWorkToDo = CreateSemaphore(NULL, 0, MAXQUEUE, "WorkToDo");
	hExit = CreateEvent(NULL, TRUE, FALSE, "Exit");

	InitializeCriticalSection(&m_CriticalSection);

	m_nTopIndex = 0;
	m_nBottomIndex = 0;
	m_nWorkInProgress = 0;

	// create pointer array for the queue
	m_pQueue = new CPoolJob* [MAXQUEUE];
}

CThreadPool::~CThreadPool()
{}


void CThreadPool::initPool(int nMaxNumberThreads)
{
	this->m_MaxNumThreads = nMaxNumberThreads;

	if( NULL == m_hThread )
		m_hThread = new HANDLE [m_MaxNumThreads];

	for( int i=0; i<m_MaxNumThreads; i++ )
		m_hThread[i] = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, this, 0, (unsigned *)&m_Thrdaddr);
}


void CThreadPool::destroyPool()
{
	SetEvent(hExit);

	Sleep(500);

	DeleteCriticalSection(&m_CriticalSection);

	CloseHandle(hEmptySlot);
	CloseHandle(hWorkToDo);
	CloseHandle(hExit);

	delete m_hThread;
	delete m_pQueue;
}

unsigned __stdcall CThreadPool::ThreadFunc(void *pArg)
{
	DWORD tid = GetCurrentThreadId();

	printf("[%d]: Thread Enter\n", tid);
	((CThreadPool *)pArg)->DoWork();
	printf("[%d]: Thread Exit\n", tid);

	return 0;
}


void CThreadPool::DoWork()
{
	CPoolJob *job = 0;

	while(GetWork(&job)) {
		printf("Get Job!\n");
		job->RunningJob();
		printf("End Job!\n");

		// elliminate a job finished
		delete job;
		//free(job);
		job = NULL;

		InterlockedDecrement(&m_nWorkInProgress);
	}
}


bool CThreadPool::GetWork(CPoolJob **job)
{
	HANDLE	hWaitHandles[2];
	DWORD	ret;

	hWaitHandles[0] = hWorkToDo;
	hWaitHandles[1] = hExit;

	ret = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE);

	if( 1 == (ret-WAIT_OBJECT_0) )
		return false;

	EnterCriticalSection(&m_CriticalSection);
	
	*job = m_pQueue[m_nBottomIndex];
	m_pQueue[m_nBottomIndex] = NULL;
	m_nBottomIndex = (m_nBottomIndex++) % (MAXQUEUE);
	ReleaseSemaphore(hEmptySlot, 1, NULL);

	LeaveCriticalSection(&m_CriticalSection);

	return true;
}

bool CThreadPool::registJob(CPoolJob *job)
{
	DWORD	ret;

	InterlockedIncrement(&m_nWorkInProgress);

	ret = WaitForSingleObject(hEmptySlot, INFINITE);

	if( WAIT_OBJECT_0 != ret )
			return false;

	EnterCriticalSection(&m_CriticalSection);

	m_pQueue[m_nTopIndex] = job;
	m_nTopIndex = (m_nTopIndex++) % (MAXQUEUE);
	ReleaseSemaphore(hWorkToDo, 1, NULL);

	LeaveCriticalSection(&m_CriticalSection);

	return true;
}

